#pragma once

namespace _hHuffmanDetail
{
	struct Node;
	struct CodeEx2Callback;

	namespace _DecodeEx2Detail
	{
		using Str = hStrView;
		using StrHash = HHash<Str>;
		using Intv = hInterval<size_t>;
		using IntvVec = std::vector<Intv>;

		using CharTab = std::map<uint8_t, size_t>;
		using CharTabIt = CharTab::iterator;

		struct FreqTabVal;
		using StrTab = hHashMap<Str, FreqTabVal, StrHash>;
		using StrTabIt = StrTab::Iter;
		using StrSort = Sort<StrTab>;
		using StrSeg = hIntervalMap<size_t, StrTabIt>;
		using Wt = std::multimap<size_t, Node*>;
		using TimeTrace = std::vector<std::pair<std::string, std::chrono::time_point<std::chrono::steady_clock>>>;

		struct FreqTabVal : public SortDetail::Item<StrTab>
		{
			size_t _freq = 0;
			Node* _node = NULL;
			hIntervalMap<size_t, size_t> _seg;

			size_t getSort() const { return _freq; }
			std::ostream& fillOs(std::ostream& os) const;
		};

		struct Freq
		{
			CodeEx2Callback* pOwner = NULL;
			const char* _inDt = NULL;
			size_t _len = 0;
			TimeTrace& _timeTrace;

			CharTab _charTab;
			Wt _charWt;
			Wt _lenWt;
			size_t _lenFreqTotal = 0;

			StrTab _strTab;
			StrSeg _strSeg;
			Wt _strWt;

			StrTab _tmpStrTab;
			StrSort _tmpStrSort = StrSort(_tmpStrTab);
			StrSeg _tmpStrSeg;

			void operator() (size_t diff);
			StrTabIt insert(size_t diff, size_t beg, size_t end, size_t minNum);
			void calc(size_t diff, size_t minNum);
			void resetTmp();
		};
	}

	struct CodeEx2Callback
	{
		hBitStr& _outData;
		const char* _inDt = NULL;
		size_t _len = 0;
		Node* _pCharTree = NULL;
		Node* _pLenTree = NULL;
		Node* _pStrTree = NULL;
		_DecodeEx2Detail::TimeTrace _timeTrace;
		_DecodeEx2Detail::Freq _freq{ this, _inDt, _len, _timeTrace };
		std::map<uint8_t, hBitStr> _charKeyTab;
		std::map<size_t, hBitStr> _lenKeyTab;

		void operator() ();

		static std::ostream& showTree(std::ostream& os, Node* pTree);
	private:
		void init();
		void show();
		void fin();
		void code();

		void genCharTree();
		void genLenTree();
		void genStrTree();

		void genCharKey(Node* pTree = NULL, hBitStr val = hBitStr{});
		void genLenKey(Node* pTree = NULL, hBitStr val = hBitStr{});
		void genStrKey(Node* pTree = NULL, hBitStr val = hBitStr{});

		void codeCharTree(const Node* pTree = NULL);
		void codeLenTree(const Node* pTree = NULL);
		void codeStrTree(const Node* pTree = NULL);
	};

	struct DecodeEx2Callback
	{
		const hBitStr& _inBit;
		hBitView _view{ _inBit };
		size_t _len = 0;
		Node* _pCharTree = NULL;
		Node* _pLenTree = NULL;
		Node* _pStrTree = NULL;

		template <typename T>
		void operator() (T& outDt);

	private:
		void init();
		void show();
		void fin();
		void decode(std::string& outDt);

		void genCharTree(Node** pPTree = NULL);
		void genLenTree(Node** pPTree = NULL);
		void genStrTree(Node** pPTree = NULL);
		void finCb(const Node* pTree);
		size_t getLen(const Node* pTree = NULL);
		std::bitset<8> getChar(const Node* pTree = NULL);
		void showTreeCb(Node* pTree) const;
	};
}
